﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using ArgusLib.InteropServices;

namespace ArgusLib.FFmpeg.avutil
{
	public class AVVideoFrame : AVFrame
	{
		#region Constructors
		public static AVVideoFrame CreateInstance(AVPixelFormat pixelFormat, int width, int height)
		{
#if Log
			Log.WriteLine("Allocate AVVideoFrame...");
#endif
			AVVideoFrame RetVal = Interop.Functions.av_videoframe_alloc();
			RetVal.PixelFormat = pixelFormat;
			RetVal.Width = width;
			RetVal.Height = height;
#if Log
			Log.WriteLine("Get AVVideoFrame Buffer...");
#endif
			int error = Interop.Functions.av_frame_get_buffer(RetVal, 16);
#if Log
			Log.WriteLine("AVVideoFrame allocated");
#endif
			if (error < 0)
				return null;
			return RetVal;
		}

		public static AVVideoFrame FromImage(Image image)
		{
			AVPixelFormat pixelFormat = image.GetAVPixelFormat();
			if (pixelFormat == AVPixelFormat.AV_PIX_FMT_NONE)
				return null;

			AVVideoFrame frame = AVVideoFrame.CreateInstance(pixelFormat, image.Width, image.Height);
			frame.UpdateImage(image);

			return frame;
		}
		#endregion

		#region Methods
		public bool UpdateImage(Image image)
		{
			AVPixelFormat pixelFormat = image.GetAVPixelFormat();
			if (pixelFormat == AVPixelFormat.AV_PIX_FMT_NONE || pixelFormat != this.PixelFormat)
				return false;

			if (image.Width != this.Width || image.Height != this.Height)
				return false;

			using (Bitmap bitmap = new Bitmap(image))
			{
				BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, image.PixelFormat);
				Marshaler.Copy(data.Scan0, this.ImageData[0], Math.Abs(data.Stride) * data.Height);
				bitmap.UnlockBits(data);
			}
			return true;
		}
		#endregion

		#region Properties
		public AVPixelFormat PixelFormat
		{
			get { return (AVPixelFormat)this.Structure.format; }
			private set { this.Structure.format = (int)value; }
		}

		public int Width
		{
			get { return this.Structure.width; }
			private set { this.Structure.width = value; }
		}

		public int Height
		{
			get { return this.Structure.height; }
			private set { this.Structure.height = value; }
		}

		internal IntPtr[] ImageData { get { return this.Structure.data; } }
		internal int[] Strides { get { return this.Structure.linesize; } }
		#endregion
	}
}
